package com.zed.api.common.resource;

import cn.hutool.core.io.IoUtil;
import com.zed.api.common.localstore.LocalStoreUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.JarURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Enumeration;
import java.util.Objects;
import java.util.function.Predicate;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * 基于*.jar文件资源的扫描器
 *
 * @author Zed
 */
public class JarFileResourceScanner implements ResourceScanner<JarFile> {

    private static final String PROTOCOL = "jar";
    private static final String SEPARATOR = "/";

    private static final String BOOT_INF = "BOOT-INF/lib/";

    private static final Path TEMPORARY_STORAGE = LocalStoreUtils.getInstance().getWorkingDirectory().resolve("unzip");


    private Logger logger = LoggerFactory.getLogger(JarFileResourceScanner.class);

    static {
        TEMPORARY_STORAGE.toFile().mkdirs();
    }

    /**
     * 构造方法
     */
    public JarFileResourceScanner() {

    }

    @Override
    public JarFile support(URL url) {
        if (PROTOCOL.equals(url.getProtocol())) {
            try {
                JarURLConnection conn = (JarURLConnection) url.openConnection();
                String entryName = conn.getEntryName().replace(BOOT_INF, "");
                Path finalPath = TEMPORARY_STORAGE.resolve(entryName);
                File finalFile = finalPath.toFile();
                if(!finalFile.exists()){
                    try (InputStream inputStream = conn.getInputStream();
                         OutputStream op = Files.newOutputStream(finalPath)) {
                        finalFile.createNewFile();
                        IoUtil.copy(inputStream, op);
                    } catch (IOException e) {
                        return null;
                    }
                }
                return new JarFile(finalFile);
            } catch (Exception e) {
                return null;
            }
        } else {
            if (url.getProtocol().equals("file")) {
                String path = url.getPath();
                if (path.endsWith(".jar")) {
                    try {
                        return new JarFile(new File(path));
                    } catch (IOException e) {
                        return null;
                    }
                }
            }
        }

        return null;
    }

    @Override
    public void scanning(JarFile instance, String resourceDir, Predicate<String> filter, boolean subdirectory,
                         ResourceScanningCallback callback) {
        Objects.requireNonNull(instance, "\"instance\" cannot be null.");
        Objects.requireNonNull(resourceDir, "\"resourceDir\" cannot be null.");
        Objects.requireNonNull(filter, "\"filter\" cannot be null.");
        Objects.requireNonNull(callback, "\"callback\" cannot be null.");
        String path = resourceDir.startsWith(SEPARATOR) ?
                resourceDir.substring(1) : resourceDir;
        if (!path.endsWith(SEPARATOR)) {
            path = path.concat(SEPARATOR);
        }
        int length = path.length();

        Enumeration<JarEntry> em = instance.entries();
        while (em.hasMoreElements()) {
            JarEntry entry = em.nextElement();
            String name = entry.getName();
            if (name.startsWith(path)) {
                String filePath = name.substring(length);
                if (filePath.length() == 0) continue;

                if (!subdirectory) {
                    if (filePath.indexOf(SEPARATOR) != -1) {
                        continue;
                    }
                }

                if (this.doFilter(filePath, filter)) {
                    String uri = "jar:file:".concat(instance.getName()).concat("!/").concat(name);
                    try {
                        callback.onFound(filePath, new URL(uri));
                    } catch (MalformedURLException e) {
                        if (logger.isErrorEnabled()) {
                            logger.error(e.getMessage(), e);
                        }
                    }
                }
            }
        }
    }

    /**
     * 执行过滤
     *
     * @param filePath {@link String}, 资源的相对路径
     * @param filter   {@link Predicate}, 过滤器
     * @return {@link boolean}, 满足条件则返回<code>true</code>
     */
    protected boolean doFilter(String filePath, Predicate<String> filter) {
        return filter.test(filePath);
    }

}
